import React, {
  useEffect,
  useState,
  useRef,
  forwardRef,
  useImperativeHandle,
  memo,
  useMemo,
} from 'react';
import _ from 'lodash';
import { Form, Button, message } from 'antd';
import { RouteContext } from '@ant-design/pro-layout';
import DebounceWrapper from '@/components/DebounceWrapper';
import { DownOutlined, UpOutlined, SearchOutlined, SettingOutlined } from '@ant-design/icons';
import { getUsefulSearchItems, getSearchParams } from './tools';
import { fetchAvailableDictChildList } from '@/services/system/dictionary';
import styles from './index.less';

/**
 *  ref?;
 *  limitMinWidth?: number; // 单元最小宽度（取值>=240）
 *  searchLoading?: bool;
 *  onSearch?: func;
 *  formList: {
 *    key: string; // 表单值所对应的key
 *    Component: ReactNode; // react 元素
 *    searchType: "LIKE" | "IN" | "GE" | "LE" | "GT" | "LT" | "EQ" | "SORT"; // 查询类型, "SORT" => "ORDERBYASC" | "ORDERBYDESC"
 *    props?: {
 *      colon?: bool; // 表单控件名称后面是否展示"："，默认true
 *      label?: string; // 表单控件名称
 *      tooltip: string | ReactNode; // 提示信息
 *      initialValue?: any; // 表单控件初始值（值类型与组件Component有关）
 *      validateTrigger: "onChange" | "onBlur"; // 字段校验的时机
 *      dependencies?: string[]; 联动校验的依赖表单项key列表
 *      rules?:{}[]; // 校验规则
 *    }; // form.item 属性
 *    componentProps?: {
 *      allowClear?:bool; // 是否展示清除
 *      disabled?: bool; // 是否禁用，默认false
 *      min?: number; // InputNumber控件输入框最小值
 *      max?: number; // InputNumber控件输入框最大值
 *      maxLength?: number; // Input控件输入框最大长度
 *      placeholder?: string; // 占位文字
 *      format: string | string[]; // DatePicker控件日期格式
 *      disabledDate: func; // DatePicker控件时间禁用函数
 *      multiple: bool; // Cascader控件多选模式
 *      mode: "multiple" | "tags"; // Select控件选择模式
 *      maxTagCount: number | "responsive"; // Select控件在多选情况下的tag显示数
 *      options?: {label: string, value: string, disabled?: bool, children?:[]}[]; // Select、Cascader控件选项
 *      treeData?: {title: string; value: string; disabled?: bool, children?:[]}[]; // TreeSelect控件选项
 *      prefix?: string | ReactNode; // 输入框前缀
 *      suffix?: string | ReactNode; // 输入框后缀
 *      showSearch?: bool | object; // Select控件为bool值，需要搭配filterOption使用；Cascader控件为Object，配置showSearch.filter过滤函数
 *      filterOption?: func; // Select控件过滤函数
 *      onChange?: func; // change回调，不做值更新逻辑
 *
 *      ... // 省略，其他同antd表单控件属性
 *
 *      asyncOptions?: {
 *        asyncDictType?: string; // 字典类型key,自动获取选项规则，一般配合Select控件使用
 *        asyncRequest?: func; // 选项请求处理函数,自定义选项请求，并返回对应的选项属性和值，例：{options: {}[]} 或 {treeData: {}[]}
 *      }; // （异步：配置获取规则）
 *    }
 *  }[]
 * */

const DebounceButton = DebounceWrapper(Button);

const SearchItems = memo(
  forwardRef(
    (
      {
        formList,
        barCollapsed,
        maxColNum,
        searchLoading,
        initialLoading,
        formAsyncOptions,
        setBarCollapsed,
        onSearch,
      },
      ref,
    ) => {
      const searchMappings = useMemo(
        () => _.assign({}, ..._.map(formList, ({ key, searchType }) => ({ [key]: searchType }))),
        [],
      );
      const widthPercent = Math.floor((100 / maxColNum) * 10) / 10;
      const [form] = Form.useForm();

      const checkAction = () => {
        return new Promise((resolve, reject) => {
          form
            .validateFields()
            .then((values) => {
              const params = getSearchParams(values, searchMappings);
              resolve(params);
            })
            .catch((err) => {
              reject();
              const { errorFields } = err;
              let shouldOpen = false;
              const keys = _.flatten(_.map(errorFields, 'name'));
              const originalKeys = _.map(formList, 'key');
              for (let i = 0; i < originalKeys.length; i++) {
                if (_.includes(keys, originalKeys[i]) && i >= maxColNum - 1) {
                  shouldOpen = true;
                  break;
                }
              }
              if (shouldOpen && barCollapsed) {
                setBarCollapsed(false);
              }
            });
        });
      };

      const handleSearch = () => {
        checkAction()
          .then((values) => {
            onSearch && onSearch(values);
          })
          .catch(() => {
            message.warning('请设置有效条件后再查询');
          });
      };

      const handleReset = () => {
        form.resetFields();
      };

      useImperativeHandle(ref, () => ({
        getInitialSearchParams: () => {
          return new Promise((resolve, reject) => {
            checkAction()
              .then((params) => {
                resolve(params);
              })
              .catch(() => {
                reject();
              });
          });
        },
        getSearchFieldError: form.getFieldError,
        getSearchFieldsError: form.getFieldsError,
        getSearchFieldValue: form.getFieldValue,
        getSearchFieldsValue: form.getFieldsValue,
        resetSearchFields: form.resetFields,
        setSearchFields: form.setFields,
        setSearchFieldValue: form.setFieldValue,
        setSearchFieldsValue: form.setFieldsValue,
        validateSearchFields: form.validateFields,
      }));

      return (
        <Form form={form} name={`search-form`} layout="horizontal">
          {formList.map(
            (item, index) =>
              item.Component && (
                <div
                  key={`search-form-${item.key}-${item.searchType}`}
                  className={styles.searchItem}
                  style={
                    barCollapsed
                      ? index < maxColNum - 1
                        ? { width: `${widthPercent}%` }
                        : { display: 'none' }
                      : { width: `${widthPercent}%` }
                  }
                >
                  <Form.Item key={`${item.key}-${item.searchType}`} name={item.key} {...item.props}>
                    {React.createElement(item.Component, {
                      ..._.pickBy(
                        item.componentProps || {},
                        (v, k) => !_.includes(['asyncOptions'], k),
                      ),
                      ...(formAsyncOptions && _.isPlainObject(formAsyncOptions[item.key])
                        ? formAsyncOptions[item.key].props || {}
                        : {}),
                    })}
                  </Form.Item>
                </div>
              ),
          )}
          <div className={styles.searchEmptyItem} style={{ width: `${widthPercent}%` }} />
          <div className={styles.actionButton}>
            <DebounceButton
              ghost
              type="primary"
              icon={<SettingOutlined />}
              className={styles.actionButtonItem}
              onClick={() => {
                if (!searchLoading) {
                  handleReset();
                }
              }}
            >
              重置
            </DebounceButton>
            <DebounceButton
              type="primary"
              icon={<SearchOutlined />}
              className={styles.actionButtonItem}
              loading={!!searchLoading}
              onClick={handleSearch}
            >
              查询
            </DebounceButton>
            <span
              className={styles.toggleButtonItem}
              style={maxColNum <= formList.length ? {} : { display: 'none' }}
              onClick={() => {
                setBarCollapsed((status) => !status);
              }}
            >
              {barCollapsed ? <DownOutlined /> : <UpOutlined />}
              {barCollapsed ? '展开' : '收起'}
            </span>
          </div>
        </Form>
      );
    },
  ),
  (prevProps, nextProps) =>
    prevProps.maxColNum === nextProps.maxColNum &&
    prevProps.barCollapsed === nextProps.barCollapsed &&
    prevProps.searchLoading === nextProps.searchLoading &&
    prevProps.initialLoading === nextProps.initialLoading,
);

const SearchBar = memo(
  forwardRef(
    (
      {
        isMobile,
        collapsed,
        menuRender,
        containerWidth,
        formList = [],
        searchLoading = false,
        limitMinWidth = 240,
        onSearch = () => {},
      },
      ref,
    ) => {
      const barContainerRef = useRef(null);
      const [contentWidth, setContentWidth] = useState(0);
      const [barCollapsed, setBarCollapsed] = useState(true);

      // 格式化处理（获取异步子项配置）
      const { formItemList, asyncOption } = useMemo(() => {
        const itemList = getUsefulSearchItems(formList);
        const asyncOptions = _.assign(
          {},
          ..._.map(itemList, ({ key, componentProps }) =>
            _.isPlainObject(componentProps?.asyncOptions)
              ? { [key]: componentProps?.asyncOptions }
              : null,
          ),
        );
        return { formItemList: itemList, asyncOption: asyncOptions };
      }, []);
      const [initialLoading, setInitialLoading] = useState(!_.isEmpty(asyncOption));
      const [formAsyncOptions, setFormAsyncOptions] = useState(asyncOption); // 控件异步数据集合

      // 最大列数
      const maxColNum = useMemo(
        () => _.max([Math.floor((contentWidth - 20 * 2) / limitMinWidth), 1]),
        [contentWidth],
      );

      // 宽度计算
      const adaptWidth = () => {
        const currentContentWidth = barContainerRef.current?.clientWidth || containerWidth;
        setContentWidth(currentContentWidth);
        return currentContentWidth;
      };
      const adaptContentWidth = _.throttle(adaptWidth, 500);

      // 获取字典值列表：{label: string; value: string}[]
      const getDictList = async ({ key, dictType }) => {
        try {
          const res = await fetchAvailableDictChildList({ dictType });
          const optionList = _.isArray(res)
            ? _.filter(
                _.map(res, ({ dictValue, dictLabel }) => ({ value: dictValue, label: dictLabel })),
                ({ value }) => _.isString(value) || _.isNumber(value),
              )
            : [];
          return { key, props: { options: optionList } };
        } catch (e) {
          return { key, props: { options: [] } };
        }
      };
      // 执行字典属性初始化函数
      const runAsyncRequest = async ({ key, asyncRequest }) => {
        try {
          const props = await asyncRequest();
          return { key, props };
        } catch (e) {
          return { key, props: {} };
        }
      };

      // 初始化
      useEffect(() => {
        // 计算宽度
        const defaultContentWidth = adaptContentWidth();
        window.addEventListener('resize', adaptContentWidth);
        // 初始化时，若最多展示1列的情况下， 设置为默认展开
        const defaultMaxColNum = _.max([
          Math.floor((defaultContentWidth - 20 * 2) / limitMinWidth),
          1,
        ]);
        setBarCollapsed(defaultMaxColNum !== 1);

        // 初始化控件异步选项
        if (!_.isEmpty(asyncOption)) {
          const dictTypes = _.filter(
            _.map(_.keys(asyncOption), (key) => ({
              key,
              dictType: asyncOption[key].asyncDictType,
            })),
            ({ dictType }) => !!dictType,
          );
          const asyncRequestList = _.filter(
            _.map(_.keys(asyncOption), (key) => ({
              key,
              asyncRequest: asyncOption[key].asyncRequest,
            })),
            ({ asyncRequest }) => typeof asyncRequest === 'function',
          );

          if (dictTypes.length + asyncRequestList.length > 0) {
            Promise.all([
              ..._.map(dictTypes, (item) => getDictList(item)),
              ..._.map(asyncRequestList, (item) => runAsyncRequest(item)),
            ]).then((result) => {
              setFormAsyncOptions((currentOpts) => {
                const nextOpts = { ...currentOpts };
                _.forEach(result, ({ key, props }) => {
                  if (_.isPlainObject(nextOpts[key])) {
                    nextOpts[key].props = props;
                  }
                });
                return nextOpts;
              });
              setInitialLoading(false);
            });
          } else {
            setInitialLoading(false);
          }
        }

        return () => {
          window.removeEventListener('resize', adaptContentWidth, false);
        };
      }, []);
      useEffect(() => {
        setTimeout(() => {
          adaptContentWidth();
        }, 300);
      }, [isMobile, collapsed, menuRender]);

      return (
        <div
          ref={barContainerRef}
          className={styles.searchBar}
          style={{
            height: `${
              24 + 56 * (barCollapsed ? 1 : Math.ceil((formItemList.length + 1) / maxColNum))
            }px`,
          }}
        >
          <SearchItems
            ref={ref}
            formList={formItemList}
            maxColNum={maxColNum}
            barCollapsed={barCollapsed}
            searchLoading={searchLoading}
            setBarCollapsed={setBarCollapsed}
            initialLoading={initialLoading}
            formAsyncOptions={formAsyncOptions}
            onSearch={onSearch}
          />
        </div>
      );
    },
  ),
  (prevProps, nextProps) =>
    prevProps.isMobile === nextProps.isMobile &&
    prevProps.collapsed === nextProps.collapsed &&
    prevProps.menuRender === nextProps.menuRender &&
    prevProps.searchLoading === nextProps.searchLoading,
);

export default forwardRef((props, ref) => (
  <RouteContext.Consumer>
    {(partial) => {
      const { isMobile, collapsed, layout, menuRender, siderWidth } = partial || {};
      const defaultContainerWidth =
        document.body.clientWidth -
        20 * 2 -
        (isMobile || layout === 'top' || !menuRender ? 0 : collapsed ? 60 : siderWidth);
      return (
        <SearchBar
          ref={ref}
          {...props}
          isMobile={isMobile}
          collapsed={collapsed}
          menuRender={layout === 'top' ? false : !!menuRender}
          containerWidth={_.max([defaultContainerWidth, 0])}
        />
      );
    }}
  </RouteContext.Consumer>
));
